import bisect
import math

from view.endSrc.WeightedObject import WeightedObject

class WeightedObjectCollector(list):

    def __init__(self, collectMax, collectMin, collectAll=False):
        super(WeightedObjectCollector,self).__init__([])
        self.collectMax = collectMax
        self.collectMin = collectMin
        self.collectAll = collectAll
        self.minWO = None
        self.maxWO = None

    def append(self, wo: WeightedObject):
        if self.collectAll:
            # super(WeightedObjectCollector, self).append(wo)
            # bisect.insort(self, 3)
            lo = 0
            hi = len(self)
            while lo < hi:
                mid = (lo + hi) // 2
                if wo.weight < self[mid].weight:
                    hi = mid
                else:
                    lo = mid + 1
            self.insert(lo, wo)
            # MUST NOT RETURN

        if self.collectMax:
            if self.maxWO is None:
                self.maxWO = wo
            elif self.maxWO.weight < wo.weight:
                self.maxWO = wo

        if self.collectMin:
            if self.minWO is None:
                self.minWO = wo
            elif self.minWO.weight > wo.weight:
                self.minWO = wo
    def getMax(self):
        if self.collectMax:
            return self.maxWO

        # not allowed: collectMin= True, collectMax=False
        assert not self.collectMin

        #
        # collectionMax = False, collectMin = False
        #

        # collectAll can not be False <=> collectAll must be True
        assert self.collectAll

        # The list can be empty
        assert len(self) > 0

        maxw = self[0]
        for w in self:  # search the whole list
            if w.weight > maxw.weight:
                maxw = w
        return maxw
    def getMin(self):
        if self.collectMin:
            return self.minWO

        # not allowed: collectMin= False, collectMax =True
        assert not self.collectMax

        #
        # collectionMin = False, collectMax = False
        #
        assert self.collectAll

        #The list must be empty
        assert len(self) > 0
        minw = self[0]
        for w in self:
            if w.weight < minw.weight:
                minw = w
            return minw
    # def sortInAscend(self):
    #     super(WeightedObjectCollector, self).sort(key=lambda x: x.weight, reverse=False)
    def getTopMax(self, percent, floor=True):
        assert percent > 0 and percent <= 1.0 and self.collectAll
        # self.sortInAscend()

        # calculate the floor and ceil
        size = len(self)
        # print('DEB: size: ', size)

        perNum = size * percent
        perNum = math.floor(perNum) if floor else math.ceil(perNum)

        print('DEB: perNum: ', perNum)

        # check 0%
        if perNum == 0:
            return []
        return self[-perNum:]
    def getTopMin(self, percent, floor=True):
        assert percent > 0 and percent <=1.0 and self.collectAll
        # self.sortInAscend()

        # calculate the floor and ceil
        size = len(self)

        perNum = size * percent
        perNum = math.floor(perNum) if floor else math.ceil(perNum)

        return self[:perNum]

    @staticmethod
    def testTopMax():

        # wc = WeightedObjectCollector(collectMax=True, collectMin=True, collectAll=True)
        # wc = WeightedObjectCollector(collectMax=False, collectMin=True, collectAll=True)

        wc = WeightedObjectCollector(collectMax=False, collectMin=False, collectAll=True)
        for i in range(100):
            wc.append(WeightedObject(i))

        percent = 0.1
        print('...get top : ', percent)
        lst = wc.getTopMax(percent)
        for w in lst:
            print(w, end=' ')

    @staticmethod
    def testMax():
        wc = WeightedObjectCollector(collectMax=True, collectMin=False)
        for i in range(10):
            wc.append(WeightedObject(i))
        wmax = wc.getMax()
        print(wmax)
    @staticmethod
    def testTopMin():
        # wc = WeightedObjectCollector(collectMax=True, collectMin=True, collectAll=True)
        wc = WeightedObjectCollector(collectMax=False, collectMin=True, collectAll=True)
        for i in range(10):
            wc.append(WeightedObject(i))

        percent = 0.2
        print('....get top :',percent)
        lst = wc.getTopMin(percent)
        for w in lst:
            print(w, end=" ")
    @staticmethod
    def testMin():
        wc = WeightedObjectCollector(collectMax=False, collectMin=True, collectAll=False)
        for i in range(8, 10):
            wc.append(WeightedObject(i))
        wmin = wc.getMin()
        print(wmin)
    @staticmethod
    def testAppend():
        wc = WeightedObjectCollector(collectMax=True, collectMin=True, collectAll=True)
        for i in range(1):
            wc.append(WeightedObject(i))
        print(wc)

if __name__ == '__main__':

    WeightedObjectCollector.testTopMax()  # ok
    # WeightedObjectCollector.testMax()    #ok
    # WeightedObjectCollector.testTopMin()   #ok
    # WeightedObjectCollector.testMin()      #ok
